package com.gframework.core.service.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 分布式锁操作接口.
 * <p>
 * <strong>【注意】</strong>在使用分布式锁时，你必须注意和了解一下问题，否则将会出现死锁问题。
 * <p>
 * 分布式中的死锁与单机多线程的死锁不是一个概念，而是当一个业务机获得锁后，宕机了，或者redis宕机了那么就将会一直处于等待状态的死锁。
 * <br>
 * <strong>分布式锁会存在但不仅限于下列问题：</strong>
 * <ol>
 * <li>业务系统宕机，导致无法释放锁，其他节点一直处于死锁。</li>
 * <li>存储锁的分布式中间件宕机，而且其中存在处于锁定状态的锁，那么就会宕机。</li>
 * <li>如果存储锁的分布式中间件采用了集群设计，或者主从设计，并且这个集群没有采用一致性优先的设计，
 * 那么就会出现已经存在锁，但是复制不及时，导致其他节点无法得知锁的存在而失效，
 * 或者master切换导致锁获取异常等问题。</li>
 * </ol>
 * <strong>解决方案：</strong>
 * <ol>
 * <li>当前接口的设计与java的Lock接口有很大区别在于，不存在无参的lock方法，你必须指定一个超时时间。超出时间后锁会被自动释放，以防止死锁发生。</li>
 * <li>实现操作接口的子类必须能够不断地判断中间件是否宕机，以避免中间件宕机导致的死锁问题。</li>
 * <li>
 * 	解决方案一：<br>
 * 	你可以使用zookeeper这种强一致性的中间件去存储锁，但是性能可能较差。<br>
 * 	解决方案二：<br>
 * 	采用单节点方式去存储锁对象。但是为了避免单节点故障问题，通常建议不再此中间件上做锁以外的任何其他业务，并且可以采用HA高可用机制或者要有一套快速故障恢复方案。<br>
 * 	解决方案三：<br>
 * 	采用红锁算法。这需要多个毫无关联但时间必须一致（无主从集群等关系）的中间件，同时对锁进行存储，只有当大多节点都获取到锁时才算获取到锁。具体可以参考红锁算法。<br>
 * </li>
 * </ol>
 * </p>
 * </p>
 * <p>
 * 如果只是想使用简单的基本锁，可以使用 {@link ReentrantLock} 公平锁 或 synchronized 非公平锁 或 {@link ReentrantReadWriteLock} 读写锁。
 * </p>
 * 
 * @since 1.0.0
 * @author Ghwolf
 * 
 * @see ReentrantLock
 * @see ReentrantReadWriteLock
 */
public interface DistributedLock {
	/**
	 * 获取锁，如果已经处于锁定状态，则会一直堵塞到锁被释放.
	 * <p>
	 * 为了避免死锁，你必须指定一个获取锁的最大时间，如果超过了这个时间，那么锁会被自动释放。
	 * 或者你可以调用{@link #unlock(String)} 方法，来释放锁。
	 * </p>
	 * @param distributedKey 锁名称，分布式锁不能以bean作为锁对象，必须通过唯一标识符指定
	 * @param leaseTime 锁的最大拥有时间，超出了会自动释放
	 * @param unit 时间单位
	 */
	public void lock(String distributedKey,long leaseTime,TimeUnit unit) ;
	
	/**
	 * 尝试释放当前持有的锁.
	 * @param distributedKey 锁名称，分布式锁不能以bean作为锁对象，必须通过唯一标识符指定
	 * @return 如果锁不存在，则返回false，如果锁存在，则释放并返回true
	 */
	public boolean unlock(String distributedKey) ;
	/**
	 * 尝试获取锁，如果能够获取到锁，则返回true，否则立刻返回false.
	 * <p>
	 * 为了避免死锁，你必须指定一个获取锁的最大时间，如果超过了这个时间，那么锁会被自动释放。
	 * 或者你可以调用{@link #unlock(String)} 方法，来释放锁。
	 * </p>
	 * @param distributedKey 锁名称，分布式锁不能以bean作为锁对象，必须通过唯一标识符指定
	 * @param leaseTime 锁的最大拥有时间，超出了会自动释放
	 * @param unit 时间单位
	 * @return 如果获取到了则返回true，否则立刻返回false
	 */
	public boolean tryLock(String distributedKey,long leaseTime,TimeUnit unit) ;
	/**
	 * 尝试在一段时间内获取锁，如果能够获取到锁，则返回true，否则指定时间结束后返回false
	 * <p>
	 * 为了避免死锁，你必须指定一个获取锁的最大时间，如果超过了这个时间，那么锁会被自动释放。
	 * 或者你可以调用{@link #unlock(String)} 方法，来释放锁。
	 * </p>
	 * @param distributedKey 锁名称，分布式锁不能以bean作为锁对象，必须通过唯一标识符指定
	 * @param waitTimeout 获取锁的最大等待时间
	 * @param leaseTime 锁的最大拥有时间，超出了会自动释放
	 * @param unit 时间单位
	 * @return 如果获取到了则返回true，否则指定时间结束后返回false
	 * @throws InterruptedException 如果等待被中断
	 */
	public boolean tryLock(String distributedKey,long waitTimeout,long leaseTime,TimeUnit unit) throws InterruptedException;
	/**
	 * 判断当前对象是否已经处于锁定状态
	 * @param distributedKey 锁名称，分布式锁不能以bean作为锁对象，必须通过唯一标识符指定
	 * @return 已经锁定返回true，没有返回false
	 */
	public boolean isLock(String distributedKey) ;
	
}
